use plotters::prelude::RGBColor;

use crate::cache::Cache;
use crate::AnalyzerError;

const MAX_PN: u32 = 10;
const MAX_ITER: u32 = 500;

const COLOR_LIST: [RGBColor; 10] = [
    RGBColor(242, 169, 159),
    RGBColor(156, 215, 226),
    RGBColor(216, 19, 137),
    RGBColor(216, 60, 175),
    RGBColor(133, 193, 221),
    RGBColor(182, 144, 229),
    RGBColor(133, 238, 252),
    RGBColor(237, 164, 149),
    RGBColor(125, 239, 31),
    RGBColor(221, 97, 194),
];

/// 生成一个长度为 `len` 的颜色序列
fn gen_colors_list(len: usize) -> Vec<RGBColor> {
    use random_color::RandomColor;

    let mut colors = vec![];

    for _i in 0..len {
        let color = RandomColor::new().to_rgb_array();
        colors.push(RGBColor(color[0] as u8, color[1] as u8, color[2] as u8));
    }

    println!("{:?}", colors);
    colors
}

pub struct LineList {
    pub name: String,
    pub list: Vec<f64>,
}

impl LineList {
    pub fn get(&self, x: usize) -> f64 {
        self.list[x]
    }
}

pub fn draw<T>(
    file: T,
    title: &str,
    max_x: u64,
    min_y: f64,
    max_y: f64,
    list: Vec<LineList>,
) -> Result<(), AnalyzerError>
where
    T: AsRef<std::path::Path>,
{
    use plotters::prelude::*;

    let root_area = BitMapBackend::new(file.as_ref(), (1024, 768)).into_drawing_area();
    root_area.fill(&WHITE).unwrap();

    let mut ctx = ChartBuilder::on(&root_area)
        .set_label_area_size(LabelAreaPosition::Left, 64)
        .set_label_area_size(LabelAreaPosition::Bottom, 64)
        .set_label_area_size(LabelAreaPosition::Right, 64)
        .caption(&title, ("mono", 32))
        .build_cartesian_2d(0..max_x, min_y..max_y)
        .unwrap();

    ctx.configure_mesh().draw().unwrap();

    let colors = if list.len() < 10 {
        COLOR_LIST.to_vec()
    } else {
        gen_colors_list(MAX_PN as usize)
    };

    for i in 0..list.len() {
        let cur_color = colors[i as usize];

        ctx.draw_series(LineSeries::new(
            (1..max_x).map(|x| (x, list[i as usize].get((x - 1) as usize))),
            (&cur_color).stroke_width(2),
        ))
        .unwrap()
        .label(&list[i].name)
        .legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 20, y + 2)], &cur_color));
    }

    ctx.configure_series_labels()
        .border_style(&BLACK)
        .background_style(&WHITE.mix(0.8))
        .draw()
        .unwrap();

    Ok(())
}

pub fn draw_dot<T>(
    file: T,
    title: &str,
    max_x: u64,
    min_y: f64,
    max_y: f64,
    list: Vec<LineList>,
) -> Result<(), AnalyzerError>
where
    T: AsRef<std::path::Path>,
{
    use plotters::prelude::*;

    let root_area = BitMapBackend::new(file.as_ref(), (1024, 768)).into_drawing_area();
    root_area.fill(&WHITE).unwrap();

    let mut ctx = ChartBuilder::on(&root_area)
        .set_label_area_size(LabelAreaPosition::Left, 64)
        .set_label_area_size(LabelAreaPosition::Bottom, 64)
        .set_label_area_size(LabelAreaPosition::Right, 64)
        .caption(&title, ("mono", 32))
        .build_cartesian_2d(0..max_x, min_y..max_y)
        .unwrap();

    ctx.configure_mesh().draw().unwrap();

    for i in 0..list.len() {
        for j in 0..list[i].list.len() {
            ctx.draw_series(PointSeries::of_element(
                vec![(j as u64, list[i].list[j])],
                2,
                &BLACK,
                &|c, s, st| {
                    return EmptyElement::at(c) + Circle::new((0, 0), s, st.filled());
                },
            ))
            .unwrap();
        }
    }

    ctx.configure_series_labels()
        .border_style(&BLACK)
        .background_style(&WHITE.mix(0.8))
        .draw()
        .unwrap();

    Ok(())
}

pub fn draw_pn<T>(file: T, cache: &Cache, pm: f64, pc: f64) -> Result<(), AnalyzerError>
where
    T: AsRef<std::path::Path>,
{
    let mut list = Vec::new();
    for i in 1..MAX_PN {
        log::info!("Prefetch cache {} {} {}", pm, pc, i * 10);
        let _ = cache.get_value(i * 10, pm, pc, MAX_ITER);

        let line = LineList {
            name: format!("pn={}", i * 10),
            list: (1..MAX_ITER)
                .map(|x| cache.get_value(i * 10, pm, pc, x).unwrap())
                .collect(),
        };

        list.push(line);
    }

    draw(
        file,
        &format!("pm={}, pc={}", pm, pc),
        MAX_ITER as u64,
        150.,
        450.,
        list,
    )?;

    Ok(())
}
